from collections import OrderedDict
from os.path import dirname, join, abspath
import logging
import pkgutil
import fnmatch
import re
import copy

logger = logging.getLogger('forest.resource')
logger.setLevel(logging.DEBUG)

registry = {}

def register_resource_type(name, cls):
    registry[name] = cls

def get_resource_class_by_name(name):
    if name not in registry:
        raise Exception("Unknown resource type %s" % name)

    return registry.get(name)

class ValidationError(Exception):
    pass

class Resources(object):
    def __init__(self, config, resources):
        self.config = config
        self.resources = OrderedDict()
        self.filters = []

        # convert the list of resources into Resource instances
        for resource in resources:

            if isinstance(resource, basestring):
                r = {}
                match = re.match('(\w+)\((.+?)\)', resource)
                r['$'], r['name'] = match.group(1), match.group(2)
                resource = r

            resource_copy = copy.deepcopy(resource)
            del resource_copy["$"]

            resource_instance = get_resource_class_by_name(resource.get('$'))(self.config, resource_copy)

            self.add_or_update(resource_instance)

        #TODO remove
        self.validate()

    def validate(self):
        for k,resource_instance in self.resources.iteritems():
            try:
                resource_instance.validate()
            except ValidationError:
                print "Error validating resource: ", resource_instance.get_id()
                raise

    def add_or_update(self, resource_instance):
        if resource_instance.get_id() in self.resources:
            self.resources[resource_instance.get_id()] += resource_instance
        else:
            self.resources[resource_instance.get_id()] = resource_instance

    def add_filter(self, pattern):
        self.filters.append(re.compile( fnmatch.translate(pattern)))

    def clear_filters(self):
        self.filters = []

    def iter_all(self):
        for k,v in self.resources.iteritems():
            if self.filters:
                for f in self.filters:
                    if f.match(v.get_id()):
                        yield v
            else:
                yield v

    def iter_by_type(self, type):
        for k,v in self.resources.iteritems():
            if self.filters:
                for f in self.filters:
                    if f.match(v.get_id()) and v.__register__ == type:
                        yield v
            else:
                if v.__register__ == type:
                    yield v

    def __add__(self, other):
        for r in other.iter_all():
            self.add_or_update(r)
        return self

    def serialize(self):
        resource_list = []
        for r in self.iter_all():
            r_value = copy.copy(r.value)
            r_value['$'] = r.get_resource_type()
            resource_list.append(r_value)
        return resource_list

class ResourceMeta(type):
    """Metaclass for adding a Resource to the registry.
    """
    def __init__(cls, name, bases, dic):
        super(ResourceMeta, cls).__init__(name, bases, dic)

        if cls is ResourceMeta:
            return

        if cls.__name__ != "Resource":
            register_name = dic.get('__register__')

            if not register_name:
                raise Exception("Could not find declared __register__ property on Resource class %s" % cls.__name__)

            register_resource_type(register_name, cls)

class Resource(object):
    __metaclass__ = ResourceMeta

    __resource__ = ''

    def __init__(self, config, value):
        self.config = config
        self.value = value

    def get_config(self):
        ''' Return the original configuration object this resource is associated with.

            Used for path translation for file resources.
        '''
        return self.config

    def get_value(self):
        return self.value

    def get_resource_type(self):
        #TODO prob old broken code
        return self.__register__

    def get_id(self):
        ''' Each resource should be uniquely identifiable in some manner '''
        raise NotImplementedError

    def validate(self):
        pass

    def __repr__(self):
        return '%s(%s, %s)' % (self.__register__, self.value, self.config.url.path)#self.config.get_dirname())

    def __add__(self, other):
        ''' Adding resources of the same type should yield one merged resource. '''
        self.value = dict(self.value.items() + other.value.items())
        return self


def search_resources(path, prefix):
    logger.debug('Searching for plugins under %s...', path)
    paths = [path]

    for importer, modname, ispkg in pkgutil.walk_packages(path=paths, prefix=prefix, onerror=lambda x: None):
        module = __import__(modname, fromlist="dummy")
        logger.debug('Imported module %s.', module)

    return registry

search_resources(join(dirname(abspath(__file__)), 'resources'), 'forest.resources.')
